import { ControlNode } from "./ControlNode";
import { NodeStatus } from "../NodeStatus";
import { INodeConfig } from "../interfaces/INodeConfig";


/**
 * 当返回SUCCESS的子节点个数>=THRESHOLD_SUCCESS时，返回SUCCESS。
 * 当返回FAILURE的子节点个数>=THRESHOLD_FAILURE时，返回FAILURE。
 * 当程序判断绝不可能SUCCESS时，返回FAILURE。如 failure_children_num > children_count - success_threshold_。
 */
export class ParallelNode<T> extends ControlNode<T> {

    private success_threshold: number = 0;
    private failure_threshold: number = 1;
    private __skip_list: Set<number>;

    constructor(name: string, blackboard: T) {
        super(name, blackboard);
        this.__skip_list = new Set();
    }

    Init(data: INodeConfig): void {
        if (data.hasOwnProperty('success')) {
            //@ts-ignore
            this.success_threshold = data["success"];
        }
        if (data.hasOwnProperty('failure')) {
            //@ts-ignore
            this.failure_threshold = data["failure"];
        }
        this.success_threshold = Math.max(this.success_threshold, 0);
        this.failure_threshold = Math.max(this.failure_threshold, 1);
    }

    Tick(): NodeStatus {
        let success_childred_num = 0;
        let failure_childred_num = 0;

        const children_count = this.$children.length;

        if (children_count < this.success_threshold) {
            throw new Error('Number of children is less than threshold. Can never succeed.');
        }

        if (children_count < this.failure_threshold) {
            throw new Error('Number of children is less than threshold. Can never fail.');
        }

        // Routing the tree according to the sequence node's logic:
        for (let i = 0; i < children_count; i++) {
            const child_node = this.$children[i];

            const in_skip_list = this.__skip_list.has(i);

            let child_status: NodeStatus;
            if (in_skip_list) {
                child_status = child_node.status;
            } else {
                child_status = child_node.Evaluate();
            }

            switch (child_status) {
                case NodeStatus.SUCCESS:
                    {
                        if (!in_skip_list) {
                            this.__skip_list.add(i);
                        }
                        success_childred_num++;

                        if (success_childred_num == this.success_threshold) {
                            this.__skip_list.clear();
                            this.HaltChildren();
                            return NodeStatus.SUCCESS;
                        }
                    }
                    break;
                case NodeStatus.FAILURE:
                    {
                        if (!in_skip_list) {
                            this.__skip_list.add(i);
                        }
                        failure_childred_num++;

                        // It fails if it is not possible to succeed anymore or if
                        // number of failures are equal to failure_threshold_
                        if (
                            failure_childred_num > children_count - this.success_threshold ||
                            failure_childred_num == this.failure_threshold
                        ) {
                            this.__skip_list.clear();
                            this.HaltChildren();
                            return NodeStatus.FAILURE;
                        }
                    }
                    break;
                case NodeStatus.RUNNING:
                    {
                        // do nothing
                    }
                    break;
                default: {
                    throw new Error('A child node must never return IDLE');
                }
            }
        }
        return NodeStatus.RUNNING;
    }

    Halt(): void {
        this.__skip_list.clear();
        super.Halt();
    }

    Destroy(): void {
        this.__skip_list.clear();
        super.Destroy();
    }
}